---
title: "FK Leveling"
type: concept
created: 2026-04-18
updated: 2026-04-18
sources: ["raw/articles/06-reading-telemetry.md", "raw/notes/memory.md"]
tags: [adaptive-content, fk-leveling, zpd, gpt-4o, api]
---

# FK Leveling

FK Leveling is the core algorithmic primitive of the [[Adaptive Engine]]. It uses the Flesch-Kincaid readability formula to target a specific reading difficulty, then calls GPT-4o to rewrite any passage to that target level.

## The ZPD Offset

The system never targets the child's *current* level — it targets slightly above it:

```
targetFkLevel = child.reading_level + 0.5
```

This +0.5 offset encodes the **Zone of Proximal Development** (Vygotsky): material just beyond current ability promotes growth without causing frustration. The cap prevents runaway leveling:

```
targetFkLevel = min(child.reading_level + 0.5, 5.5)
```

The FK scale maps roughly to US grade levels: FK 3.0 = third grade, FK 5.5 = mid-fifth grade. The 5.5 cap is appropriate for the Pickatale age range (4–11).

## POST /api/v1/level-page

This is the primary endpoint. The [[Reader App]] calls it on every page render.

**Request body:**
```json
{
  "book_id": "14760-1",
  "page_number": 3,
  "source_text": "The magnificent elephant wandered across the savannah...",
  "target_fk_grade": 3.5,
  "learner_id": "uuid"
}
```

**Cache key:** `book_id + page_number + round(target_fk_grade, 1) + sha256(source_text)`

Rounding `target_fk_grade` to one decimal place means FK 3.47 and 3.52 hit the same cache slot — this prevents cache fragmentation for nearly identical levels.

**Short text bypass:** If `source_text` is fewer than 30 words, FK scoring is unreliable. The engine returns the original text unchanged without calling GPT-4o.

**Retry logic:** After GPT-4o returns a leveled version, the engine scores the result. If `|actual_fk - target_fk| > 0.5`, it retries once with a tighter prompt. On second failure it returns the first attempt and logs the discrepancy.

**Response:**
```json
{
  "leveled_text": "The big elephant walked across the open land...",
  "actual_fk": 3.4,
  "from_cache": false
}
```

## POST /api/v1/adapt

This endpoint offers more control than `level-page` and is used by internal pipelines (lesson book generation, test content):

- `target_fk_grade` is `DECIMAL(4,2)` — supports two decimal places for finer control
- Cache tolerance is `±0.1` FK grades instead of `±0.5`
- Includes `vocab_hints: string[]` — words the engine must preserve in the output

The stricter tolerance makes this endpoint slower (more cache misses, more GPT calls) but more precise. It is not called on every page render.

## Known Issue: FK Scores Not Changing in Practice

⚠️ **Known Issue (2026-04-18):** In observed runs, the GPT-4o leveling step sometimes returns text with FK scores far from the target. Example: target 3.5 → actual FK 20.87 (a college-level score).

**Root cause analysis:** GPT-4o is not reliably optimizing for the FK formula. The formula weights syllable counts and sentence lengths in ways that are non-intuitive for a language model.

**Three options under consideration:**

| Option | Approach | Trade-off |
|---|---|---|
| 1. Tighten prompt | Add explicit word-length and sentence-length constraints to the system prompt | May improve scores but adds prompt complexity |
| 2. Stricter validation | Reject outputs where actual FK deviates >0.5 from target; retry up to 3× | Higher latency; no guarantee of convergence |
| 3. Accept approximation | Treat the engine as a "best effort" leveler; log deviations; improve over time | Easiest short-term, degrades child experience for outlier books |

**Current status:** Option 3 in practice (engine is live), with logging. Options 1 and 2 are candidates for the next engineering sprint.

⏳ **DECISION-XX:** Which scoring fix strategy to adopt — pending Jixian + Sig review.

## Provider Configuration

| Provider | Status | Notes |
|---|---|---|
| GPT-4o | ✅ Enabled | Primary leveling and translation |
| DeepL | ❌ Unavailable | No API key configured |
| Google Translate | ❌ Unavailable | No API key configured |

Translation is architecturally planned (single GPT call handles both leveling + translation) but not deployable until keys are provisioned.

## Related Pages

- [[concepts/adaptive-content/index|Adaptive Content Engine]] — overview and pipeline
- [[concepts/adaptive-content/Content Engine]] — full adapt pipeline with caching diagram
